<?php
/**
 * Created by PhpStorm.
 * User: chenpeixin
 * Date: 2017/10/25
 * Time: 下午2:01
 */

namespace App\Logging;

use Illuminate\Support\Facades\Config;
use Psr\Log\LoggerInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger as MonoLogger;

/**
 * 日志类
 *
 * Class Logger
 * @package App\Logging
 */
final class Logger implements LoggerInterface
{
    // 命令
    protected static $command = null;

    // 打开过的日志
    protected static $cachedLoggers = [];

    // 名称栈
    protected static $nameStack = [];

    // 当前名称
    protected static $current = 'lumen';

    // logid
    protected static $logId = '<log id>';

    // prefix
    protected static $prefix = '';

    // logger
    protected $logger = null;

    /**
     * 设置命令行模式
     *
     * @param mixed $command 命令类实例
     */
    public static function setCommand($command)
    {
        self::$command = $command;
    }

    /**
     * 先进先出名称 进
     *
     * @param string $name 名称
     */
    public static function pushName($name)
    {
        array_push(self::$nameStack, self::$current);
        self::setName($name);
    }

    /**
     * 先进先出名称 出
     */
    public static function popName()
    {
        $name = array_pop(self::$nameStack);
        if ($name === null) {
            $name = 'lumen';
        }
        self::setName($name);
    }

    /**
     * 设置名称
     *
     * @param string $name 名称
     */
    public static function setName($name)
    {
        self::$current = $name;
    }

    /**
     * 设置logid
     *
     * @param string $logId logid
     */
    public static function setLogId($logId)
    {
        self::$logId = $logId;
    }

    /**
     * 设置prefix
     *
     * @param string $prefix prefix
     */
    public static function setPrefix($prefix)
    {
        self::$prefix = $prefix;
    }

    /**
     * append prefix
     *
     * @param string $prefix prefix
     */
    public static function appendPrefix($prefix)
    {
        self::$prefix .= $prefix;
    }

    /**
     * 获取当面logger
     *
     * @return mixed
     */
    public static function getCurrentLogger()
    {
        return self::getLogger(self::$current);
    }

    /**
     * 获取命名logger
     *
     * @param string $name 名称
     *
     * @return mixed
     */
    public static function getLogger($name)
    {
        if (!isset(self::$cachedLoggers[$name])) {
            self::$cachedLoggers[$name] = new static(self::createLogger($name));
        }

        return self::$cachedLoggers[$name];
    }

    /**
     * 获取日志文件全路径
     *
     * @param string $name 名称
     *
     * @return string
     */
    protected static function getFilename($name)
    {
        $file = Config::get(sprintf('logging.%s.file', $name), $name);

        return storage_path('logs/'.$file.'.log');
    }

    /**
     * 获取日志文件级别
     *
     * @param string $name 名称
     *
     * @return int
     */
    protected static function getLevel($name)
    {
        $levelName = Config::get(sprintf('logging.%s.level', $name), 'INFO');

        $map = [
            'DEBUG'     => MonoLogger::DEBUG,
            'INFO'      => MonoLogger::INFO,
            'NOTICE'    => MonoLogger::NOTICE,
            'WARNING'   => MonoLogger::WARNING,
            'ERROR'     => MonoLogger::ERROR,
            'CRITICAL'  => MonoLogger::CRITICAL,
            'ALERT'     => MonoLogger::ALERT,
            'EMERGENCY' => MonoLogger::EMERGENCY,
        ];

        $level = MonoLogger::INFO;
        if (array_key_exists($levelName, $map)) {
            $level = $map[$levelName];
        }

        return $level;
    }

    /**
     * 创建 日志 handler
     *
     * @param string $name 名称
     *
     * @return mixed
     */
    protected static function createHandler($name)
    {
        return new RotatingFileHandler(self::getFilename($name), self::getLevel($name));
    }

    /**
     * 创建 日志 logger
     *
     * @param string $name 名称
     *
     * @return mixed
     */
    protected static function createLogger($name)
    {
        $handler = self::createHandler($name);
        $handler->setFormatter(new LineFormatter(null, null, true, true));

        return new MonoLogger($name, [$handler]);
    }

    /**
     * 构造函数
     *
     * @param mixed $logger logger
     */
    protected function __construct($logger)
    {
        $this->logger = $logger;
    }

    /**
     * 打印多行
     *
     * @param \Closure $closure closure
     * @param string   $message mesasge
     *
     * @return mixed
     */
    protected function logMultipleLine(\Closure $closure, $message)
    {
        if (self::$command !== null) {
            self::$command->info((string)$message);
        }

        $messageString = (string)$message;
        $messageLines = explode("\n", $messageString);
        $newMessageLines = [];

        $i = 0;
        foreach ($messageLines as $messageLine) {
            if ($i == 0) {
                $newMessageLines[] = sprintf('%s:%s%s', self::$logId, self::$prefix, $messageLine);
            } else {
                $newMessageLines[] = sprintf('# %s:%s%s', self::$logId, self::$prefix, $messageLine);
            }
            $i += 1;
        }

        return $closure(implode("\n", $newMessageLines));
    }

    /**
     * 打印
     *
     * @param int    $level level
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function log($level, $message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($level, $context) {
            $this->logger->log($level, $msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function debug($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->debug($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function info($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->info($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function notice($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->notice($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function warn($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->warn($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function warning($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->warning($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function err($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->err($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function error($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->error($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function crit($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->crit($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function critical($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->critical($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function alert($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->alert($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function emerg($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->emerg($msg, $context);
        }, $message);
    }

    /**
     * 打印
     *
     * @param string $message mesasge
     * @param array  $context context
     *
     * @return mixed
     */
    public function emergency($message, array $context = array())
    {
        return $this->logMultipleLine(function ($msg) use ($context) {
            $this->logger->emergency($msg, $context);
        }, $message);
    }

    /**
     * __call
     *
     * @param string $name name
     * @param array  $arguments arguments
     *
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        return call_user_func_array([$this->logger, $name], $arguments);
    }
}
